package serv

import (
	"encoding/json"
	"erp/app"
	"erp/middleware"
	"erp/model"
	"erp/utils"
	"fmt"
	"log"
	"os"
	"reflect"
	"runtime/debug"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
)

type FWebServer struct {
	gin.Context
}

func CallFuncs(fc interface{}, params ...interface{}) (result interface{}, err error) {
	f := reflect.ValueOf(fc)
	if f.Kind() != reflect.Func {
		err = fmt.Errorf("param fc is not avaliablel, must be func")
		return
	}
	if len(params) != f.Type().NumIn() {
		err = fmt.Errorf("the number of params is not adapted")
		return
	}

	in := make([]reflect.Value, len(params))
	for k, param := range params {
		in[k] = reflect.ValueOf(param)
	}
	resp := f.Call(in)
	if len(resp) > 0 {
		result = reflect.ValueOf(resp[0].Interface()).Interface()
		return
	}
	result = nil
	return
}

/**
 * 字符串首字母转化为大写 ios_bbbbbbbb -> iosBbbbbbbbb
 */
func strFirstToUpper(str string) string {
	str = "_" + str
	temp := strings.Split(str, "_")
	var upperStr string
	for y := 0; y < len(temp); y++ {
		vv := []rune(temp[y])
		if y != 0 {
			for i := 0; i < len(vv); i++ {
				if i == 0 {
					vv[i] -= 32
					upperStr += string(vv[i]) // + string(vv[i+1])
				} else {
					upperStr += string(vv[i])
				}
			}
		}
	}
	return temp[0] + upperStr
}

var dbEngine = app.GetDbEngine()

func StartServer() {
	gin.SetMode("release")

	r := gin.Default().Delims("${", "}")

	r.GET("/login.html", LoginCtrl)
	r.GET("/logout.html", LoginoutCtrl)
	r.POST("/login.html", LoginCheckCtrl)

	r.GET("/admin/order/exportExcel", AdminOrderExportExcelCtrl)
	r.GET("/admin/order/exportExcel1", AdminOrderExportExcel1Ctrl)

	r.GET("/:model/:dir/:file", func(c *gin.Context) {

		AuthCheck(c)

		model := c.Param("model")
		dir := c.Param("dir")
		file := c.Param("file")

		if !strings.Contains(file, ".html") {
			file = file + ".html"
		}
		tpl := ""
		checkFile := ""
		if model == "admin" {
			tpl = dir + "-" + file
			checkFile = "./templates/" + dir + "/" + dir + "-" + file
		} else {
			tpl = "user" + "-" + dir + "-" + file
			checkFile = "./templates/user/" + tpl
		}

		if !utils.IsPathExist(checkFile) {
			c.Header("Content-Type", "text/html; charset=utf-8")
			c.String(404, "<h1>404 NOT FOUND</h1>"+checkFile+" not found")
			log.Println("\n---------------ROUNTE ERROR---------------\n⚡ " + checkFile + "  not found\n------------------------------------------ ")
			return
		}

		c.HTML(200, tpl, gin.H{})
	})

	r.POST("/:model/:dir/:file", func(c *gin.Context) {

		AuthCheck(c)

		model := c.Param("model")
		dir := c.Param("dir")
		file := c.Param("file")
		file = strings.ReplaceAll(file, ".html", "")

		funcs := map[string]interface{}{
			"AdminBaseGetRoleMenusCtrl": AdminBaseGetRoleMenusCtrl,

			"AdminUploadSaveCtrl":   AdminUploadSaveCtrl,
			"AdminUploadEditorCtrl": AdminUploadEditorCtrl,

			"AdminRoleUpdateCtrl": AdminRoleUpdateCtrl,
			"AdminRoleDeleteCtrl": AdminRoleDeleteCtrl,

			"AdminSysGetMenuCtrl":        SysGetMenuCtrl,
			"AdminRoleIndexCtrl":         AdminRoleIndexCtrl,
			"AdminRoleGetUpdateInfoCtrl": AdminRoleGetUpdateInfoCtrl,
			"AdminLogIndexCtrl":          AdminLogIndexCtrl,
			"AdminLogDeleteCtrl":         AdminLogDeleteCtrl,

			"AdminNodeIndexCtrl":         AdminNodeIndexCtrl,
			"AdminNodeAddCtrl":           AdminNodeUpdateCtrl,
			"AdminNodeUpdateCtrl":        AdminNodeUpdateCtrl,
			"AdminNodeDeleteCtrl":        AdminNodeDeleteCtrl,
			"AdminNodeGetFieldListCtrl":  AdminNodeGetFieldListCtrl,
			"AdminNodeGetUpdateInfoCtrl": AdminNodeGetUpdateInfoCtrl,

			"AdminAdminUserIndexCtrl": AdminUserCtrl,
			// "AdminAdminUserAddCtrl":           AdminUserUpdateCtrl,
			"AdminAdminUserUpdateCtrl":        AdminUserUpdateCtrl,
			"AdminAdminUserDeleteCtrl":        AdminUserDeleteCtrl,
			"AdminAdminUserGetUpdateInfoCtrl": AdminUserGetUpdateInfoCtrl,
			"AdminAdminUserGetFieldListCtrl":  AdminNodeGetFieldListCtrl,
			"AdminAdminUserResetPwdCtrl":      AdminUserResetPwdCtrl,

			"AdminGoodsListCtrl":          AdminGoodsListCtrl,
			"AdminGoodsSaveCtrl":          AdminGoodsSaveCtrl,
			"AdminGoodsCateUpdateCtrl":    AdminGoodsCateUpdateCtrl,
			"AdminGoodsCateListCtrl":      AdminGoodsCateListCtrl,
			"AdminGoodsGetCateCtrl":       AdminGoodsGetCateCtrl,
			"AdminGoodsDeleteCateCtrl":    AdminGoodsDeleteCateCtrl,
			"AdminGoodsGetUpdateInfoCtrl": AdminGoodsGetUpdateInfoCtrl,

			"AdminOrderIndexCtrl": AdminOrderIndexCtrl,

			"AdminOrgIndexCtrl":         AdminOrgIndexCtrl,
			"AdminOrgGetUpdateInfoCtrl": AdminOrgGetUpdateInfoCtrl,
			"AdminOrgUpdateCtrl":        AdminOrgUpdateCtrl,
			"AdminOrgDeleteCtrl":        AdminOrgDeleteCtrl,
			"AdminOrgUpdateExtCtrl":     AdminOrgUpdateExtCtrl,
			"AdminOrgGetFieldListCtrl":  AdminOrgGetFieldListCtrl,
			"AdminOrgAddCtrl":           AdminOrgUpdateCtrl,

			"UserCartAddCtrl":         UserCartAddCtrl,
			"UserCartIndexCtrl":       UserCartIndexCtrl,
			"UserCartDeleteCtrl":      UserCartDeleteCtrl,
			"UserCartCreateOrderCtrl": UserCartCreateOrderCtrl,
			"UserProfileDetailCtrl":   UserProfileDetailCtrl,

			"UserOrderIndexCtrl": UserOrderIndexCtrl,
		}

		str := strFirstToUpper("_" + model + "_" + dir + "_" + file + "_ctrl")
		// fmt.Print("POST--------- ", str, " ")

		_, err := CallFuncs(funcs[str], c)
		if err != nil {
			conf := 0  // 配置、终端默认设置
			bg := 0    // 背景色、终端默认设置
			text := 31 // 前景色、红色
			fmt.Printf("\n %c[%d;%d;%dm%s%c[0m\n\n", 0x1B, conf, bg, text, "找不到控制器："+str, 0x1B)
			// c.JSON(404, "找不到控制器："+str)
			c.JSON(200, map[string]any{"status": 404, "msg": "找不到控制器：" + str})
			return
		}
	})

	port := app.Config().Port

	r.Use(middleware.CrossSite)
	r.Use(middleware.NewMidLogger())
	r.Use(errorHttp)

	r.GET("/", IndexCtrl)

	r.Static("/wx", "./wx")
	r.Static("/static", "./public/static")
	r.Static("/assets", "./public/assets")
	r.Static("/uploads", "./public/uploads")
	r.Static("/export", "./public/export")
	r.StaticFile("/favicon.ico", "./static/favicon.ico")
	r.LoadHTMLGlob("templates/**/*.html")

	//记录pid
	os.WriteFile("server.sock", []byte(fmt.Sprintf("%d,%d", os.Getppid(), os.Getpid())), 0666)

	r.Run(fmt.Sprintf(":%d", port))
}

func ApiErr(c *gin.Context, err error) {
	c.JSON(200, gin.H{"status": "err", "msg": err})
	log.Panicln(err)
}

// errorHttp 统一500错误处理函数
func errorHttp(c *gin.Context) {
	defer func() {
		if r := recover(); r != nil {
			//打印错误堆栈信息
			log.Printf("panic: %v\n", r)
			debug.PrintStack()
			//封装通用json返回
			c.HTML(200, "500.html", gin.H{
				"title": "500",
			})
		}
	}()
	c.Next()
}

// 模板载入函数
// func loadTemplates(templatesDir string) multitemplate.Renderer {
//   r := multitemplate.NewRenderer()

//   bases, err := filepath.Glob(templatesDir + "/*.html")
//   if err != nil {
//     panic(err.Error())
//   }

//   includes, err := filepath.Glob(templatesDir + "/**/*.html")
//   if err != nil {
//     panic(err.Error())
//   }

//   for _, include := range includes {
//     baseCopy := make([]string, len(bases))
//     copy(baseCopy, bases)
//     files := append(baseCopy, include)
//     r.AddFromFiles(strings.Replace(include, "templates/", "", 1), files...)
//   }

//   return r
// }

func AuthCheck(c *gin.Context) {
	token, _ := c.Cookie("token")
	// token := c.GetHeader("token")
	if token == "" {
		token = c.Query("token")
	}

	if token == "" {
		c.Redirect(302, "/login.html")
		c.Abort()
		return
	}

	userinfo := ParseToken(token)
	if userinfo.Id == 0 {
		c.Redirect(302, "/login.html")
		c.Abort()
		return
	}

	c.Set("userData", userinfo)
	c.Set("userId", userinfo.Id)
}

func ParseToken(token string) model.AdminUser {
	token_arr := strings.Split(token, "|")
	userData := model.AdminUser{}

	if len(token_arr) == 3 {
		userId, _ := strconv.Atoi(token_arr[0])

		if userId > 0 {
			userData = userData.Get(userId)
		}
	} else {
		log.Println("没有取到客户度Token")
	}
	return userData
}

func GetJsonData(c *gin.Context) map[string]any {
	raw, _ := c.GetRawData()
	var param map[string]any
	json.Unmarshal(raw, &param)
	return param
}
